home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / oldwish / wishDisplay.c < prev    next >
C/C++ Source or Header  |  1990-01-23  |  56KB  |  1,805 lines

  1. /* 
  2.  * wishDisplay.c --
  3.  *
  4.  *    Routines for layout and display for the file system flat display.
  5.  *
  6.  * Copyright 1987 Regents of the University of California
  7.  * All rights reserved.
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /a/newcmds/wish/RCS/wishDisplay.c,v 1.6 89/01/19 16:53:10 mgbaker Exp Locker: mgbaker $ SPRITE (Berkeley)";
  19. #endif not lint
  20.  
  21. typedef    int    Boolean;
  22. #define    FALSE    0
  23. #define TRUE    1
  24. #include <sys/types.h>
  25. #include <sys/stat.h>
  26. #include "time.h"
  27. #include <sys/dir.h>    /* included only for MAXNAMLEN */
  28. #include "string.h"
  29. #include "sx.h"
  30. #include "util.h"
  31. #include "monitorClient.h"
  32. #include "wishInt.h"
  33.  
  34. /*
  35.  * Include cursor declarations and definitions.
  36.  * flat.cursor defines the regular flat display cursor and wait.cursor defines
  37.  * the cursor used while the display is busy.
  38.  */
  39. #include "flat.cursor"
  40. #include "wait.cursor"
  41.  
  42. /*
  43.  * The flat cursor is the regular cursor when the display is
  44.  * idle.  The wait cursor indicates that the display is working
  45.  * away on something.  The regular cursor can be redefined by
  46.  * users, if they wish. (Well... sometime that will be possible...)
  47.  */
  48. static        Cursor        flatCursor = -1;
  49. static        Cursor        waitCursor = -1;
  50.  
  51. /* Default commands in the overall command table. */
  52. static    CmdInfo    commands[] = {
  53.     {"bind",        WishBindCmd},
  54.     {"changeDirectory",    WishChangeDirCmd},
  55.     {"quit",        WishQuitCmd},
  56.     {"close",        WishCloseCmd},
  57.     {"redraw",        WishRedrawCmd},
  58.     {"selection",    WishSelectionCmd},
  59.     {"resize",        WishResizeCmd},
  60.     {"toggleSelection",    WishToggleSelectionCmd},
  61.     {"toggleSelEntry",    WishToggleSelEntryCmd},
  62.     {"groupBind",    WishGroupBindCmd},
  63.     {"open",        WishOpenCmd},
  64.     {"sortFiles",    WishSortFilesCmd},
  65.     {"setFields",    WishChangeFieldsCmd},
  66.     {"defineGroup",    WishDefineGroupCmd},
  67.     {"changeGroup",    WishChangeGroupCmd},
  68.     {"pattern",        WishPatternCompareCmd},
  69.     {"menu",        WishMenuCmd},
  70.     {"exec",        WishExecCmd},
  71.     {(char *) NULL,    (int (*)()) NULL}
  72. };
  73.  
  74. /*
  75.  * Number of open windows, for graceful exit when we close them all.
  76.  */
  77. int    wishWindowCount = 0;
  78.  
  79. /*
  80. /*
  81.  * Used before defined.
  82.  */
  83. static    void    GetColumnWidthInfo();
  84. static    int    GetMaxEntryWidth();
  85. static    void    FigureWindowSize();
  86.  
  87.  
  88.  
  89. /*
  90.  *----------------------------------------------------------------------
  91.  *
  92.  * WishCreate --
  93.  *
  94.  *    Create an wish window.
  95.  *
  96.  * Results:
  97.  *    A pointer to the created WishWindow structure, or NULL if the
  98.  *    call failed.
  99.  *
  100.  * Side effects:
  101.  *    A new wish window will be created and displayed.
  102.  *
  103.  *----------------------------------------------------------------------
  104.  */
  105. WishWindow *
  106. WishCreate(aWindow, dir)
  107.     WishWindow    *aWindow;    /* parent window */
  108.     char        *dir;        /* directory to start in */
  109. {
  110.     WishWindow    *newWindow;
  111.     struct    stat    dirAtts;
  112.     Tx_WindowInfo    info;
  113.     int            result;
  114.     static        char    *args[] = {"/bin/csh", "-i", NULL};
  115.     XSizeHints        sizeHints;
  116.     XGCValues        gcValues;
  117.  
  118.     newWindow = (WishWindow *) malloc(sizeof (WishWindow));
  119.     if (dir != NULL) {
  120.     /* quick hack until i fix selection problems */
  121.     if (dir[strlen(dir) - 1] == ' ') {
  122.         dir[strlen(dir) - 1] = '\0';
  123.     }
  124.     if (Util_CanonicalDir(dir, NULL, newWindow->dir) == NULL) {
  125.         /* error message returned in newWindow->dir */
  126.         /* if null, then we were called from main() anyway */
  127.         if (aWindow->interp == NULL) {
  128.         Sx_Panic(wishDisplay, newWindow->dir);
  129.         }
  130.         strcpy(aWindow->interp->result, newWindow->dir);
  131.         free(newWindow);
  132.         return NULL;
  133.     }
  134.     } else {
  135.     strcpy(newWindow->dir, aWindow->dir);
  136.     }
  137.     if (lstat(newWindow->dir, &dirAtts)
  138.         != 0) {
  139.     /* if null, then we were called from main() anyway */
  140.     if (aWindow->interp == NULL) {
  141.         sprintf(wishErrorMsg,
  142.             "Can't get attributes for %s.  Maybe it doesn't exist",
  143.             newWindow->dir);
  144.         Sx_Panic(wishDisplay, wishErrorMsg);
  145.     }
  146.     sprintf(aWindow->interp->result,
  147.         "Can't get attributes for %s.  Maybe it doesn't exist",
  148.         newWindow->dir);
  149.     free(newWindow);
  150.     return NULL;
  151.     }
  152.     /* Put name of dir in the editable string for the title display window */
  153.     strcpy(newWindow->editDir, newWindow->dir);
  154.  
  155.     if ((dirAtts.st_mode & S_IFMT) != S_IFDIR) {    /* not a directory */
  156.     if (aWindow->interp == NULL) {
  157.         sprintf(wishErrorMsg, "%s is not a directory", newWindow->dir);
  158.         Sx_Panic(wishDisplay, wishErrorMsg);
  159.     }
  160.     sprintf(aWindow->interp->result, "%s is not a directory",
  161.         newWindow->dir);
  162.     free(newWindow);
  163.     return NULL;
  164.     }
  165.      
  166.     if (chdir(newWindow->dir) != 0) {
  167.     if (aWindow->interp == NULL) {
  168.         sprintf(wishErrorMsg,
  169.             "Couldn't change directories to %s", newWindow->dir);
  170.         Sx_Panic(wishDisplay, wishErrorMsg);
  171.     }
  172.     sprintf(aWindow->interp->result,
  173.         "Couldn't change directories to %s", newWindow->dir);
  174.     free(newWindow);
  175.     return NULL;
  176.     }
  177.     strcpy(wishCurrentDirectory, newWindow->dir);
  178.  
  179.     newWindow->foreground = aWindow->foreground;
  180.     newWindow->background = aWindow->background;
  181.     newWindow->selection = aWindow->selection;
  182.     newWindow->border = aWindow->border;
  183.     newWindow->borderWidth = aWindow->borderWidth;
  184.     newWindow->fontPtr = aWindow->fontPtr;
  185.     newWindow->titleForeground = aWindow->titleForeground;
  186.     newWindow->titleBackground = aWindow->titleBackground;
  187.     newWindow->titleBorder = aWindow->titleBorder;
  188.     newWindow->titleFontPtr = aWindow->titleFontPtr;
  189.     newWindow->txForeground = aWindow->txForeground;
  190.     newWindow->txBackground = aWindow->txBackground;
  191.     newWindow->txBorder = aWindow->txBorder;
  192.     newWindow->menuForeground = aWindow->menuForeground;
  193.     newWindow->menuBackground = aWindow->menuBackground;
  194.     newWindow->sortForeground = aWindow->sortForeground;
  195.     newWindow->sortBackground = aWindow->sortBackground;
  196.     newWindow->fieldsForeground = aWindow->fieldsForeground;
  197.     newWindow->fieldsBackground = aWindow->fieldsBackground;
  198.     newWindow->entryForeground = aWindow->entryForeground;
  199.     newWindow->entryBackground = aWindow->entryBackground;
  200.     newWindow->scrollForeground = aWindow->scrollForeground;
  201.     newWindow->scrollBackground = aWindow->scrollBackground;
  202.     newWindow->scrollElevator = aWindow->scrollElevator;
  203.     newWindow->geometry = aWindow->geometry;
  204.     newWindow->firstElement = UNINITIALIZED;
  205.     newWindow->numGroups = UNINITIALIZED;
  206.     newWindow->numHiddenGroups = 0;
  207.     newWindow->hideEmptyGroupsP = aWindow->hideEmptyGroupsP;
  208.     newWindow->dontDisplayChangesP = FALSE;
  209.     newWindow->notifierP = FALSE;
  210.  
  211.     /*
  212.      * Create the first window and subwindows and use the window id to
  213.      * identify the application's data.
  214.      */
  215.     if (newWindow->geometry != NULL) {
  216.     (void) XParseGeometry(newWindow->geometry, &sizeHints.x, &sizeHints.y,
  217.         &sizeHints.width, &sizeHints.height);
  218.     }
  219.     newWindow->surroundingWindow = XCreateSimpleWindow(wishDisplay,
  220.         DefaultRootWindow(wishDisplay), sizeHints.x, sizeHints.y,
  221.     sizeHints.width, sizeHints.height, newWindow->borderWidth,
  222.         newWindow->border, newWindow->background);
  223.     if (newWindow->surroundingWindow == 0) {
  224.     if (aWindow->interp == NULL) {
  225.         Sx_Panic(wishDisplay, "Couldn't create a new window.");
  226.     }
  227.     sprintf(aWindow->interp->result, "couldn't create a new window");
  228.     goto cleanUp;
  229.     }
  230.     /* for window icon */
  231.     XStoreName(wishDisplay, newWindow->surroundingWindow, newWindow->dir);
  232.  
  233.     /* Create textGc */
  234.     gcValues.foreground = aWindow->foreground;
  235.     gcValues.background = aWindow->background;
  236.     gcValues.font = newWindow->fontPtr->fid;
  237.     newWindow->textGc = XCreateGC(wishDisplay, newWindow->surroundingWindow,
  238.         GCForeground | GCBackground | GCFont, &gcValues);
  239.     /* Create reverseGc */
  240.     gcValues.foreground = aWindow->background;
  241.     gcValues.background = aWindow->selection;
  242.     gcValues.font = newWindow->fontPtr->fid;
  243.     newWindow->reverseGc = XCreateGC(wishDisplay,
  244.         newWindow->surroundingWindow,
  245.         GCForeground | GCBackground | GCFont, &gcValues);
  246.  
  247.  
  248.     /*
  249.      * To get a nice divider between the title/entry window and the display
  250.      * window, create the windows with no border and create a divider as
  251.      * a separate, thin, window.  The title window is also a command entry
  252.      * window which is why I don't just use Sx_TitleCreate().
  253.      */
  254.     newWindow->titleWindow = Sx_CreatePacked(wishDisplay,
  255.         newWindow->surroundingWindow,
  256.         SX_TOP, Sx_DefaultHeight(wishDisplay, newWindow->titleFontPtr),
  257.         0, 0, newWindow->titleBorder, (Window) 0,
  258.         newWindow->titleBackground);
  259.     if (newWindow->titleWindow == 0) {
  260.     if (aWindow->interp == NULL) {
  261.         Sx_Panic(wishDisplay, "Couldn't create title window.");
  262.     }
  263.     sprintf(aWindow->interp->result, "couldn't create title window");
  264.     goto cleanUp;
  265.     }
  266.     /* Put "title" in window. */
  267.     Sx_EntryMake(wishDisplay, newWindow->titleWindow,
  268.         newWindow->surroundingWindow, "Directory:  ",
  269.         newWindow->titleFontPtr, newWindow->titleForeground,
  270.         newWindow->titleBackground, newWindow->editDir,
  271.         sizeof (newWindow->editDir));
  272.  
  273.     newWindow->txOutsideWindow = Sx_CreatePacked(wishDisplay,
  274.         newWindow->surroundingWindow,
  275.         SX_BOTTOM, 8 * Sx_DefaultHeight(wishDisplay, newWindow->fontPtr),
  276.         0, 0, newWindow->txBorder, (Window) 0, newWindow->txBackground);
  277.  
  278.     /* Unless i can get this info from Sx, how else can I do it? */
  279.     {
  280.     Window    dummy1;
  281.     int    x, y, width, height, border_width, dummy2;
  282.  
  283.     if (XGetGeometry(wishDisplay, newWindow->txOutsideWindow, &dummy1,
  284.         &x, &y, &width, &height, &border_width, &dummy2) == 0) {
  285.         Sx_Panic(wishDisplay, "Couldn't get tx window geometry.");
  286.     }
  287.     info.width = width;
  288.     }
  289.  
  290.     /* Create first divider */
  291.     newWindow->divider1Window = Sx_CreatePacked(wishDisplay,
  292.         newWindow->surroundingWindow,
  293.         SX_TOP, 1, 0, 0, newWindow->border, (Window) 0, newWindow->border);
  294.     if (newWindow->divider1Window == 0) {
  295.     if (aWindow->interp == NULL) {
  296.         Sx_Panic(wishDisplay,
  297.             "Couldn't create the first border between windows.");
  298.     }
  299.     sprintf(aWindow->interp->result,
  300.         "couldn't create the first border between windows");
  301.     goto cleanUp;
  302.     }
  303.     newWindow->menuBar = Sx_CreatePacked(wishDisplay,
  304.         newWindow->surroundingWindow, SX_TOP,
  305.         Sx_DefaultHeight(wishDisplay, newWindow->titleFontPtr), 0, 0,
  306.         newWindow->menuForeground, (Window) 0, newWindow->menuBackground);
  307.     /* Create next divider */
  308.     newWindow->divider2Window = Sx_CreatePacked(wishDisplay,
  309.         newWindow->surroundingWindow,
  310.         SX_TOP, 1, 0, 0, newWindow->border, (Window) 0, newWindow->border);
  311.     if (newWindow->divider2Window == 0) {
  312.     if (aWindow->interp == NULL) {
  313.         Sx_Panic(wishDisplay,
  314.             "Couldn't create the second border between windows.");
  315.     }
  316.     sprintf(aWindow->interp->result,
  317.         "couldn't create the second border between windows");
  318.     goto cleanUp;
  319.     }
  320.     newWindow->sortWindow = Sx_TitleCreate(wishDisplay,
  321.     newWindow->surroundingWindow,
  322.     SX_TOP, Sx_DefaultHeight(wishDisplay, newWindow->fontPtr), 0,
  323.     newWindow->fontPtr,
  324.     newWindow->sortForeground, newWindow->sortBackground,
  325.     newWindow->sortBackground, NULL, NULL, NULL);
  326.     if (newWindow->sortWindow == 0) {
  327.     if (aWindow->interp == NULL) {
  328.         Sx_Panic(wishDisplay, "Couldn't create the sort window.");
  329.     }
  330.     sprintf(aWindow->interp->result, "couldn't create the sort window");
  331.     goto cleanUp;
  332.     }
  333.     newWindow->divider3Window = Sx_CreatePacked(wishDisplay,
  334.         newWindow->surroundingWindow,
  335.         SX_TOP, 1, 0, 0, newWindow->border, (Window) 0, newWindow->border);
  336.     if (newWindow->divider3Window == 0) {
  337.     if (aWindow->interp == NULL) {
  338.         Sx_Panic(wishDisplay,
  339.             "Couldn't create the third border between windows.");
  340.     }
  341.     sprintf(aWindow->interp->result,
  342.         "couldn't create the third border between windows");
  343.     goto cleanUp;
  344.     }
  345.     newWindow->fieldsWindow = Sx_TitleCreate(wishDisplay,
  346.     newWindow->surroundingWindow,
  347.     SX_TOP, Sx_DefaultHeight(wishDisplay, newWindow->fontPtr), 0,
  348.     newWindow->fontPtr,
  349.     newWindow->fieldsForeground, newWindow->fieldsBackground,
  350.     newWindow->fieldsBackground, NULL, NULL, NULL);
  351.     if (newWindow->fieldsWindow == 0) {
  352.     if (aWindow->interp == NULL) {
  353.         Sx_Panic(wishDisplay, "Couldn't create the fields window.");
  354.     }
  355.     sprintf(aWindow->interp->result, "couldn't create fields window");
  356.     goto cleanUp;
  357.     }
  358.     /* Create next divider */
  359.     newWindow->divider4Window = Sx_CreatePacked(wishDisplay,
  360.         newWindow->surroundingWindow,
  361.         SX_TOP, 1, 0, 0, newWindow->border, (Window) 0, newWindow->border);
  362.     if (newWindow->divider4Window == 0) {
  363.     if (aWindow->interp == NULL) {
  364.         Sx_Panic(wishDisplay,
  365.             "Couldn't create the fourth border between windows.");
  366.     }
  367.     sprintf(aWindow->interp->result,
  368.         "couldn't create the fourth border between windows");
  369.     goto cleanUp;
  370.     }
  371.  
  372.     /*
  373.      * Is 1 really the size I want for the scrollbar border?
  374.      */
  375.     newWindow->scrollWindow = Sx_ScrollbarCreate(wishDisplay,
  376.         newWindow->surroundingWindow,
  377.         SX_RIGHT, 1, newWindow->scrollForeground,
  378.         newWindow->scrollBackground,
  379.         newWindow->scrollElevator, newWindow->border, WishScroll,
  380.         newWindow);
  381.     if (newWindow->scrollWindow == 0) {
  382.     if (aWindow->interp == NULL) {
  383.         Sx_Panic(wishDisplay, "Couldn't create the scroll bar.");
  384.     }
  385.     sprintf(aWindow->interp->result, "couldn't create the scrollbar");
  386.     goto cleanUp;
  387.     }
  388.     /* Create next divider */
  389.     newWindow->divider5Window = Sx_CreatePacked(wishDisplay,
  390.         newWindow->surroundingWindow, SX_RIGHT, 1, 0, 0, newWindow->border,
  391.         (Window) 0, newWindow->border);
  392.     if (newWindow->divider5Window == 0) {
  393.     if (aWindow->interp == NULL) {
  394.         Sx_Panic(wishDisplay,
  395.             "Couldn't create the fifth border between windows.");    
  396.     }
  397.     sprintf(aWindow->interp->result,
  398.         "couldn't create the fifth border between windows");
  399.     goto cleanUp;
  400.     }
  401. #ifdef NOTDEF
  402. /* what was this for in X10? */
  403.     tx_RegisterPtr = FALSE;
  404. #endif /* NOTDEF */
  405.     info.source = NULL;
  406.     info.fontPtr = newWindow->fontPtr;
  407.     /*
  408.      * outside of tx window is 8 * Sx_DefaultHeight, but inside is more or
  409.      * less 4 times, if you count title bars, etc.
  410.      */
  411.     info.height = 4 * Sx_DefaultHeight(wishDisplay, newWindow->fontPtr);
  412.     info.foreground = newWindow->txForeground;
  413.     info.background = newWindow->txBackground;
  414.     info.border = newWindow->txBorder;
  415.     info.sbForeground = newWindow->scrollForeground;
  416.     info.sbBackground = newWindow->scrollBackground;
  417.     info.sbElevator = newWindow->scrollElevator;
  418.     info.titleForeground = newWindow->titleForeground;
  419.     info.titleBackground = newWindow->titleBackground;
  420.     info.titleStripe = newWindow->titleForeground;
  421.     info.title = NULL;
  422.     info.flags = TX_NO_TITLE;
  423.     result = Tx_SetupAndFork(aWindow->interp, wishDisplay,
  424.         newWindow->txOutsideWindow, &info, args);
  425.     if (result != TCL_OK) {
  426.     if (aWindow->interp == NULL) {
  427.         Sx_Panic(wishDisplay,
  428.             "Couldn't set up Tx window.");
  429.     }
  430.     sprintf(aWindow->interp->result,
  431.         "couldn't set up Tx window");
  432.     goto cleanUp;
  433.     }
  434.     
  435.     /* Create next divider */
  436.     newWindow->divider6Window = Sx_CreatePacked(wishDisplay,
  437.         newWindow->surroundingWindow,
  438.         SX_BOTTOM, 1, 0, 0, newWindow->border, (Window) 0,
  439.         newWindow->border);
  440.     if (newWindow->divider6Window == 0) {
  441.     if (aWindow->interp == NULL) {
  442.         Sx_Panic(wishDisplay,
  443.             "Couldn't create the sixth border between windows.");
  444.     }
  445.     sprintf(aWindow->interp->result,
  446.         "couldn't create the sixth border between windows");
  447.     goto cleanUp;
  448.     }
  449.     /* Create display subwindow */
  450.     newWindow->displayWindow = Sx_CreatePacked(wishDisplay,
  451.         newWindow->surroundingWindow,
  452.         SX_TOP, 0, 1, 0, newWindow->border, (Window) 0,
  453.         newWindow->background);
  454.     if (newWindow->displayWindow == 0) {
  455.     if (aWindow->interp == NULL) {
  456.         Sx_Panic(wishDisplay, "Couldn't create the display window.");
  457.     }
  458.     sprintf(aWindow->interp->result, "couldn't create display window");
  459. cleanUp:
  460.     if (newWindow->surroundingWindow != NULL) {
  461.         XDestroySubwindows(wishDisplay, newWindow->surroundingWindow);
  462.         XDestroyWindow(wishDisplay, newWindow->surroundingWindow);
  463.     }
  464.     free(newWindow);
  465.     return NULL;
  466.     }
  467.  
  468.     /*
  469.      * Increment the window reference count and enter the window information
  470.      * structure into the wishWindowContext.
  471.      */
  472.     wishWindowCount++;
  473.     XSaveContext(wishDisplay, newWindow->surroundingWindow,
  474.         (caddr_t) wishWindowContext, newWindow);
  475.  
  476.     /*
  477.      * Start up the display with newWindow->dir as the directory of the display.
  478.      * WishInit() will map the surrounding window.  It may first change
  479.      * its size.
  480.      */
  481.     WishInit(newWindow);
  482.     newWindow->firstElement = 1;    /* start displaying from beginning */
  483.     /*
  484.      * WishSetPositions() and WishRedraw() will be called as a result
  485.      * of the handlers, since Sx stuff generates an ExposureMask event.
  486.      * This means I don't need to call them here explicitly.
  487.      */
  488.  
  489.     /* For the handlers, should I pass the window or newWindow as clientData? */
  490.     Sx_HandlerCreate(wishDisplay, newWindow->surroundingWindow,
  491.         EnterWindowMask, WishHandleEnterEvent, newWindow);
  492.     /*
  493.      * Select these button events in the display window, but pass the
  494.      * surrounding window to the handlers.
  495.      */
  496.     Sx_HandlerCreate(wishDisplay, newWindow->displayWindow, ButtonPressMask
  497.         | PointerMotionMask | LeaveWindowMask, WishMouseEvent,
  498.         newWindow->surroundingWindow);
  499.     /*
  500.      * Select these key press events in the title subwindow, but
  501.      * pass the surrounding window to the handlers.
  502.      */
  503.     Sx_HandlerCreate(wishDisplay, newWindow->titleWindow, KeyPressMask,
  504.         WishEditDir, newWindow->surroundingWindow);
  505.  
  506.     /*
  507.      * Move this further up before various datastructures are created?
  508.      * Add dir to wish's list.  Use windowID to match this dir for this
  509.      * window.
  510.      */
  511.     if (!MonClient_AddDir(newWindow->dir, newWindow->surroundingWindow)) {
  512.     /* What should I do here? */
  513.     Sx_Panic(wishDisplay,
  514.         "File system monitor failed in WishCreate().");
  515.     }
  516.  
  517.     return newWindow;
  518. }
  519.  
  520.  
  521. /*
  522.  *----------------------------------------------------------------------
  523.  *
  524.  * WishInit --
  525.  *
  526.  *    Initialize a new flat display.
  527.  *
  528.  * Results:
  529.  *    None.
  530.  *
  531.  * Side effects:
  532.  *    Lots.
  533.  *
  534.  *----------------------------------------------------------------------
  535.  */
  536. void
  537. WishInit(aWindow)
  538.     WishWindow    *aWindow;    /* info for this instance of display */
  539. {
  540.     XColor    cursorForeground;
  541.     XColor    cursorBackground;
  542.     Pixmap    source;
  543.  
  544.     cursorForeground.pixel = aWindow->background;
  545.     cursorBackground.pixel = aWindow->foreground;
  546.     XQueryColor(wishDisplay, DefaultColormap(wishDisplay,
  547.         DefaultScreen(wishDisplay)), &cursorForeground);
  548.     XQueryColor(wishDisplay, DefaultColormap(wishDisplay,
  549.         DefaultScreen(wishDisplay)), &cursorBackground);
  550.     if (flatCursor == -1) {
  551.     source = XCreateBitmapFromData(wishDisplay,
  552.         DefaultRootWindow(wishDisplay), flatCursor_bits,
  553.         flatCursor_width, flatCursor_height);
  554.     flatCursor = XCreatePixmapCursor(wishDisplay, source, None,
  555.         &cursorBackground, &cursorForeground, flatCursor_x_hot,
  556.         flatCursor_y_hot);
  557.     XFreePixmap(wishDisplay, source);
  558.     }
  559.     /* We'll need the cursor later - don't free it */
  560.     XDefineCursor(wishDisplay, aWindow->displayWindow, flatCursor);
  561.  
  562.     /*
  563.      * When work is being done, the cursor changes to the "wait" cursor.
  564.      */
  565.     if (waitCursor == -1) {
  566.     source = XCreateBitmapFromData(wishDisplay,
  567.         DefaultRootWindow(wishDisplay), waitCursor_bits,
  568.         waitCursor_width, waitCursor_height);
  569.     waitCursor = XCreatePixmapCursor(wishDisplay, source, None,
  570.         &cursorBackground, &cursorForeground, waitCursor_x_hot,
  571.         waitCursor_y_hot);
  572.     XFreePixmap(wishDisplay, source);
  573.     }
  574.     {
  575.     Window    dummy1;
  576.     int    x, y, width, height, border_width, dummy2;
  577.  
  578.     if (XGetGeometry(wishDisplay, aWindow->displayWindow, &dummy1,
  579.         &x, &y, &width, &height, &border_width, &dummy2) == 0) {
  580.         Sx_Panic(wishDisplay, "Couldn't get display window geometry.");
  581.     }
  582.     WishSetWindowAndRowInfo(aWindow, height, width);
  583.     }
  584.  
  585.     /* initialize data structures */
  586.     aWindow->displayInstructions = 0;
  587.     aWindow->sortingInstructions = 0;
  588.     aWindow->maxNameLength = 0;        /* longest length name */
  589.     aWindow->maxEntryWidth = 0;        /* longest entry */
  590.     aWindow->numElements = -1;        /* >= 0 means already initialized */
  591.     aWindow->totalDisplayEntries = 0;
  592.     /* numRows and rowHeight set above */
  593.     aWindow->firstElement = aWindow->lastElement = -1;
  594.     /* 8 columns is probably more than plenty to start out with. */
  595.     aWindow->columns = (WishColumn *) malloc(8 * sizeof (WishColumn));
  596.     aWindow->maxColumns = 8;
  597.     aWindow->usedCol = -1;
  598.     if (wishResizeP) {
  599.     aWindow->resizeP = TRUE;
  600.     } else {
  601.     aWindow->resizeP = FALSE;
  602.     }
  603.     if (wishPickSizeP) {
  604.     aWindow->pickSizeP = TRUE;
  605.     } else {
  606.     aWindow->pickSizeP = FALSE;
  607.     }
  608.     if (wishShowEmptyGroupsP) {
  609.     aWindow->hideEmptyGroupsP = FALSE;
  610.     } else {
  611.     aWindow->hideEmptyGroupsP = TRUE;
  612.     }
  613.     aWindow->groupList = NULL;
  614.     aWindow->selectionList = NULL;
  615.     /* create command bindings table and tcl interpreter. */
  616.     WishCmdTableInit(&(aWindow->cmdTable), &(aWindow->interp),
  617.         commands, (ClientData) aWindow);
  618.  
  619.     WishSourceConfig(aWindow);
  620.  
  621.     /* figure initial size */
  622.     if (aWindow->pickSizeP) {
  623.     aWindow->pickSizeP = FALSE;        /* only the first time */
  624.     /* This next will cause the surrounding window to change shape */
  625.     FigureWindowSize(aWindow, GetMaxEntryWidth(aWindow));
  626.     }
  627.     /* map surrounding  window */
  628.     XMapWindow(wishDisplay, aWindow->surroundingWindow);
  629.  
  630.     Sx_HandlerCreate(wishDisplay, aWindow->displayWindow,
  631.         ExposureMask | StructureNotifyMask,
  632.         WishHandleDrawingEvent, (ClientData) aWindow);
  633.     Sx_HandlerCreate(wishDisplay, aWindow->displayWindow, KeyPressMask,
  634.         WishKeyProc, (ClientData) aWindow);
  635.     Sx_HandlerCreate(wishDisplay, aWindow->menuBar, EnterWindowMask,
  636.         WishMenuEntryProc, (ClientData) aWindow);
  637.  
  638.     return;
  639. }
  640.  
  641.  
  642. /*
  643.  *----------------------------------------------------------------------
  644.  *
  645.  * WishSetPositions --
  646.  *
  647.  *    Figure out and set the coordinates of things to display.
  648.  *    This means sticking things in columns and determining when
  649.  *    the display would be filled up.
  650.  *
  651.  * Results:
  652.  *    None.
  653.  *
  654.  * Side effects:
  655.  *    The coordinates change.  A new column or so may be allocated.
  656.  *
  657.  *----------------------------------------------------------------------
  658.  */
  659. void
  660. WishSetPositions(aWindow)
  661.     WishWindow    *aWindow;
  662. {
  663.     WishGroup        *tmpGroupPtr = NULL;
  664.     WishFile        *tmpFilePtr = NULL;
  665.     WishColumn    *newColumns;
  666.     Boolean        spaceP = FALSE;
  667.     int            numCols;
  668.     int            i;
  669.     int            maxEntryWidth;
  670.     int            numMappedHeaders = 0;    /* Use at end for ignoring
  671.                          * extra expose events. */
  672. #ifdef SCROLL_DEBUG
  673.     fprintf(stderr, "Entered WishSetPositions:\n");
  674.     fprintf(stderr, "\tfirstElement is %d\n",
  675.         aWindow->firstElement);
  676.     fprintf(stderr, "\tlastElement is %d\n",
  677.         aWindow->lastElement);
  678.     fprintf(stderr, "\tnumElements is %d\n",
  679.         aWindow->numElements);
  680.     fprintf(stderr, "\tcolumns used is %d\n",
  681.         aWindow->usedCol + 1);
  682. #endif SCROLL_DEBUG
  683.  
  684.     if (aWindow->notifierP) {
  685.     return;
  686.     }
  687.     if (aWindow->firstElement <= 0) {
  688.     sprintf(wishErrorMsg,
  689.         "Entered WishSetPositions with firstElement = %d",
  690.         aWindow->firstElement);
  691.     Sx_Panic(wishDisplay, wishErrorMsg);
  692.     }
  693.     /*
  694.      * Avoid exposure events from creating and resizing windows below.  The
  695.      * window will be remapped later causing WishRedraw() to be called.
  696.      */
  697.     XUnmapWindow(wishDisplay, aWindow->displayWindow);
  698.  
  699.     maxEntryWidth = GetMaxEntryWidth(aWindow);
  700.     if (aWindow->resizeP) {
  701.     FigureWindowSize(aWindow, maxEntryWidth);
  702.     }
  703.     GetColumnWidthInfo(aWindow, maxEntryWidth, &(aWindow->columnWidth),
  704.         &numCols);
  705. #ifdef SCROLL_DEBUG
  706.     fprintf(stderr, "Number of columns to use is %d\n", numCols);
  707. #endif SCROLL_DEBUG
  708.  
  709.     /* do we need to allocate more columns? */
  710.     if (numCols > aWindow->maxColumns) {
  711.     newColumns = (WishColumn *)
  712.         malloc(aWindow->maxColumns * 2 * sizeof (WishColumn));
  713.     for (i = 0; i < aWindow->maxColumns; i++) {
  714.         newColumns[i] = aWindow->columns[i];
  715.     }
  716.     free(aWindow->columns);
  717.     aWindow->columns = newColumns;
  718.     aWindow->maxColumns *= 2;
  719.     }
  720.  
  721.     /* get to the first one to display */
  722.     i = 0;
  723.     aWindow->lastElement = 0;
  724.     for (tmpGroupPtr = aWindow->groupList; tmpGroupPtr != NULL;
  725.         tmpGroupPtr = tmpGroupPtr->nextPtr) {
  726.     /*
  727.      * Check if we've hit the right element to start displaying at.  If
  728.      * so, then check that either it's okay to display headers for empty
  729.      * groups, or else this isn't an empty group.
  730.      */
  731.     if ((i + 1 == aWindow->firstElement) && (!aWindow->hideEmptyGroupsP ||
  732.         tmpGroupPtr->fileList != NULL)) {
  733.         break;
  734.     }
  735.     tmpGroupPtr->myColumn = -1;
  736.     tmpGroupPtr->x = tmpGroupPtr->y = -1;
  737.     if (tmpGroupPtr->headerWindow != UNINITIALIZED) {
  738.         XUnmapWindow(wishDisplay, tmpGroupPtr->headerWindow);
  739.     }
  740.     /* only increment if this was a visible header */
  741.     if (!aWindow->hideEmptyGroupsP || tmpGroupPtr->fileList != NULL) {
  742.         i++;
  743.     }
  744.     for (tmpFilePtr = tmpGroupPtr->fileList; tmpFilePtr != NULL;
  745.         tmpFilePtr = tmpFilePtr->nextPtr) {
  746.         if (i + 1 == aWindow->firstElement) {
  747.         break;
  748.         } else {
  749.         tmpFilePtr->x = tmpFilePtr->y = -1;
  750.         tmpFilePtr->myColumn = -1;
  751.         }
  752.         i++;
  753.     }
  754.     if (tmpFilePtr != NULL) {
  755.         break;    /* we found it */
  756.     }
  757.     if (i+1 == aWindow->firstElement) {
  758.         /*
  759.          * The next thing we would display is a space followed by a
  760.          * group header, unless the group is an empty group and we aren't
  761.          * supposed to be displaying empty groups...
  762.          */
  763.         if (!aWindow->hideEmptyGroupsP || (tmpGroupPtr->nextPtr != NULL &&
  764.             tmpGroupPtr->nextPtr->fileList != NULL)) {
  765.         spaceP = TRUE;
  766.         tmpGroupPtr = tmpGroupPtr->nextPtr;
  767.         break;
  768.         }
  769.     }
  770.     /* only increment if this would be a space after a visible header */
  771.     if (!aWindow->hideEmptyGroupsP || tmpGroupPtr->fileList != NULL) {
  772.         i++;
  773.     }
  774.     }
  775.     if (tmpGroupPtr == NULL) {        /* all groups were empty, no display */
  776.     goto finished;
  777.     }
  778.     /* We've figured out what to start displaying */
  779.  
  780.     /* Now fill in the columns */
  781.     for (aWindow->usedCol = 0; aWindow->usedCol < numCols; aWindow->usedCol++) {
  782.     aWindow->columns[aWindow->usedCol].x =
  783.         aWindow->usedCol * aWindow->columnWidth;
  784.  
  785.     for (i = 0; i < aWindow->numRows; i++) {
  786.         if (spaceP == TRUE) {
  787.         spaceP = FALSE;
  788.         continue;
  789.         }
  790.         /*
  791.          * If tmpFilePtr is NULL, we have come to a new group header.
  792.          */
  793.         if (tmpFilePtr == NULL) {
  794.         /* set tmpFilePtr to fileList for this group. */
  795.  
  796.         tmpFilePtr = tmpGroupPtr->fileList;
  797.         if (aWindow->hideEmptyGroupsP && tmpFilePtr == NULL) {
  798.             if (tmpGroupPtr->headerWindow != UNINITIALIZED) {
  799.             XUnmapWindow(wishDisplay, tmpGroupPtr->headerWindow);
  800.             }
  801.             tmpGroupPtr = tmpGroupPtr->nextPtr;
  802.             i--;    /* so that i is the same next iteration */
  803.             if (tmpGroupPtr == NULL) {    /* end of groups */
  804.             goto finished;
  805.             }
  806.             continue;
  807.         }
  808.         tmpGroupPtr->myColumn = aWindow->usedCol;
  809.         tmpGroupPtr->x = aWindow->columns[aWindow->usedCol].x;
  810.         tmpGroupPtr->y = i * aWindow->rowHeight;
  811.         strcpy(tmpGroupPtr->editHeader, tmpGroupPtr->rule);
  812.         if (tmpGroupPtr->headerWindow == UNINITIALIZED) {
  813.             /* Need to create a header window */
  814. /* necessary? */    tmpGroupPtr->entry_width = aWindow->columnWidth;
  815.             tmpGroupPtr->entry_x = tmpGroupPtr->x;
  816.             tmpGroupPtr->entry_y = tmpGroupPtr->y;
  817.             /*
  818.              * If it's the first column, then start it one pixel
  819.              * to the left, so that borders overlap.
  820.              */
  821.             if (tmpGroupPtr->myColumn > 0) {
  822.             tmpGroupPtr->headerWindow =
  823.                 Sx_EntryCreate(wishDisplay,
  824.                 aWindow->displayWindow,
  825.                 aWindow->displayWindow,
  826.                 tmpGroupPtr->x, tmpGroupPtr->y - 1,
  827.                 aWindow->columnWidth - 1,
  828.                 aWindow->rowHeight - 1, 1, NULL,
  829.                 aWindow->fontPtr, aWindow->entryForeground,
  830.                 aWindow->entryBackground,
  831.                 tmpGroupPtr->editHeader,
  832.                 sizeof (tmpGroupPtr->editHeader));
  833.             } else {
  834.             tmpGroupPtr->headerWindow =
  835.                 Sx_EntryCreate(wishDisplay,
  836.                 aWindow->displayWindow,
  837.                 aWindow->displayWindow,
  838.                 tmpGroupPtr->x - 1, tmpGroupPtr->y - 1,
  839.                 aWindow->columnWidth,
  840.                 aWindow->rowHeight - 1, 1, NULL,
  841.                 aWindow->fontPtr, aWindow->entryForeground,
  842.                 aWindow->entryBackground,
  843.                 tmpGroupPtr->editHeader,
  844.                 sizeof (tmpGroupPtr->editHeader));
  845.             }
  846.             if (tmpGroupPtr->headerWindow == 0) {
  847.             Sx_Panic(wishDisplay,
  848.                 "You've run out of windows?!  Farewell!");
  849.             }
  850.             XSaveContext(wishDisplay, tmpGroupPtr->headerWindow,
  851.                 wishWindowContext, (caddr_t) aWindow);
  852.             XSaveContext(wishDisplay, tmpGroupPtr->headerWindow,
  853.                 wishGroupWindowContext, (caddr_t) tmpGroupPtr);
  854. fprintf(stderr, "WishEditRule set as handler, hdr wind. is %d\n", tmpGroupPtr->headerWindow);
  855.             Sx_HandlerCreate(wishDisplay,
  856.                 tmpGroupPtr->headerWindow, KeyPressMask,
  857.                 WishEditRule, tmpGroupPtr->headerWindow);
  858.         } else {
  859.             /* is the window in a new place? */
  860.             if (tmpGroupPtr->entry_x != tmpGroupPtr->x ||
  861.                 tmpGroupPtr->entry_y != tmpGroupPtr->y ||
  862.                 tmpGroupPtr->entry_width !=
  863.                 aWindow->columnWidth) {
  864.             XWindowChanges    changes;
  865.  
  866.             tmpGroupPtr->entry_x = tmpGroupPtr->x;
  867.             tmpGroupPtr->entry_y = tmpGroupPtr->y;
  868.             tmpGroupPtr->entry_width = aWindow->columnWidth;
  869.             if (tmpGroupPtr->myColumn > 0) {
  870.                 changes.x = tmpGroupPtr->x;
  871.                 changes.y = tmpGroupPtr->y - 1;
  872.                 changes.width = aWindow->columnWidth - 1;
  873.                 changes.height = aWindow->rowHeight - 1;
  874.             } else {
  875.                 changes.x = tmpGroupPtr->x - 1;
  876.                 changes.y = tmpGroupPtr->y - 1;
  877.                 changes.width = aWindow->columnWidth;
  878.                 changes.height = aWindow->rowHeight - 1;
  879.             }
  880.             XConfigureWindow(wishDisplay,
  881.                 tmpGroupPtr->headerWindow,
  882.                 CWX | CWY | CWWidth | CWHeight, &changes);
  883.             }
  884.         }
  885.         /* in case it wasn't visible last time */
  886.         XMapWindow(wishDisplay, tmpGroupPtr->headerWindow);
  887.         numMappedHeaders++;    /* another header window mapped */
  888.         /*
  889.          * If fileList is NULL for this group, move to next group.
  890.          * If this new group isn't null, it means we need another space.
  891.          * Otherwise, we're finished with all the groups.
  892.          */
  893.         if (tmpFilePtr != NULL) {
  894.             continue;
  895.         }
  896.         /*
  897.           * The group's fileList is null, move on to new group.
  898.          * We can only get here with a null fileList if
  899.          * aWindow->hideEmptyGroupsP is FALSE.
  900.          */
  901.         tmpGroupPtr = tmpGroupPtr->nextPtr;
  902.         if (tmpGroupPtr == NULL) {
  903.             /* end of groups */
  904.             aWindow->lastElement = i + 1;
  905.             break;
  906.         }
  907.         /* leave tmpFilePtr NULL so that we know we're between groups */
  908.         spaceP = TRUE;
  909.         continue;
  910.         }
  911.         /* tmpFilePtr was not NULL, so set positions for the file name. */
  912.         tmpFilePtr->x = aWindow->columns[aWindow->usedCol].x;
  913.         tmpFilePtr->y = i * aWindow->rowHeight;
  914.         tmpFilePtr->myColumn = aWindow->usedCol;
  915.  
  916.         /* move on to next file */
  917.         tmpFilePtr = tmpFilePtr->nextPtr;
  918.         if (tmpFilePtr == NULL) {
  919.         /* end of group */
  920.         tmpGroupPtr = tmpGroupPtr->nextPtr;
  921.         if (tmpGroupPtr == NULL) {
  922.             /* end of groups */
  923.             aWindow->lastElement = i + 1;
  924.             break;
  925.         }
  926.         spaceP = TRUE;
  927.         }
  928.         continue;
  929.     }
  930.  
  931.     /* We're at the end of a column */
  932.     if (tmpGroupPtr == NULL) {
  933.         /* end of groups */
  934.         aWindow->usedCol++;        /* so it ends one bigger than used */
  935.         break;
  936.     }
  937.     }
  938.  
  939.     aWindow->usedCol--;        /* loop leaves it one too big */
  940. finished:
  941.  
  942.     /* mark the rest as not visible */
  943.     for ( ; tmpGroupPtr != NULL; tmpGroupPtr = tmpGroupPtr->nextPtr) {
  944.     /* If we stopped at a header, mark it as not visible */
  945.     if (tmpFilePtr == NULL) {
  946.         tmpGroupPtr->myColumn = -1;
  947.         tmpGroupPtr->x = tmpGroupPtr->y = -1;
  948.         if (tmpGroupPtr->headerWindow != UNINITIALIZED) {
  949.         XUnmapWindow(wishDisplay, tmpGroupPtr->headerWindow);
  950.         }
  951.         tmpFilePtr = tmpGroupPtr->fileList;
  952.     }
  953.  
  954.     for ( ; tmpFilePtr != NULL; tmpFilePtr = tmpFilePtr->nextPtr) {
  955.         tmpFilePtr->x = tmpFilePtr->y = -1;
  956.         tmpFilePtr->myColumn = -1;
  957.     }
  958.     }
  959.  
  960.     /* The number of the last element visible -- includes spaces and headers */
  961.     if (aWindow->lastElement == 0) {
  962.     aWindow->lastElement = aWindow->firstElement - 1 +
  963.         ((aWindow->usedCol + 1) * aWindow->numRows);
  964.     } else {
  965.     /* We're partway into a column, add on columns up to this one */
  966.     aWindow->lastElement += aWindow->firstElement - 1 +
  967.         (aWindow->usedCol * aWindow->numRows);
  968.     }
  969.  
  970.     /* Get rid of extraneous redraw events from mapping(?) Sx entry windows. */
  971.     for (i = 0; i < numMappedHeaders; i++) {
  972.     XEvent    tossEvent;    /* dispose of this extra event */
  973.  
  974.     XCheckWindowEvent(wishDisplay, aWindow->displayWindow, ExposureMask,
  975.         &tossEvent);
  976.     }
  977.  
  978.     /* This causes WishRedraw() to be called, so the whole thing appears. */
  979.     XMapWindow(wishDisplay, aWindow->displayWindow);
  980.  
  981. #ifdef SCROLL_DEBUG
  982.     fprintf(stderr, "At end of WishSetPositions:\n");
  983.     fprintf(stderr, "\tfirstElement is %d\n",
  984.         aWindow->firstElement);
  985.     fprintf(stderr, "\tlastElement is %d\n",
  986.         aWindow->lastElement);
  987.     fprintf(stderr, "\tnumElements is %d\n",
  988.         aWindow->numElements);
  989.     fprintf(stderr, "\tnumGroups is %d\n", aWindow->numGroups);
  990.     fprintf(stderr, "\tnumRows is %d\n", aWindow->numRows);
  991.     fprintf(stderr, "\tcolumns used is %d\n",
  992.         aWindow->usedCol + 1);
  993. #endif SCROLL_DEBUG
  994.  
  995.     return;
  996. }
  997.  
  998.  
  999. /*
  1000.  *----------------------------------------------------------------------
  1001.  *
  1002.  * WishRedraw --
  1003.  *
  1004.  *    Redraw display with positions already set.
  1005.  *
  1006.  * Results:
  1007.  *    None.
  1008.  *
  1009.  * Side effects:
  1010.  *    The display is redrawn.
  1011.  *
  1012.  *----------------------------------------------------------------------
  1013.  */
  1014. void
  1015. WishRedraw(aWindow)
  1016.     WishWindow    *aWindow;
  1017. {
  1018.     int    i;
  1019.     WishGroup    *tmpGroupPtr = NULL;
  1020.     WishFile    *tmpPtr = NULL;
  1021.     float    top, bottom;
  1022.     char    fields[MAXNAMLEN + (3 * 26) + 11];    /* space allocation
  1023.                              * described below */
  1024.     char    sorting[50];
  1025.     Boolean    somethingDrawnP = FALSE;
  1026.  
  1027.     if (aWindow->notifierP) {
  1028.     return;
  1029.     }
  1030.     strcpy(fields, "Filename");
  1031.     for (i = 0; i < aWindow->maxNameLength - strlen("Filename"); i++) {
  1032.     /* Very wasteful... */
  1033.     strcat(fields, " ");
  1034.     }
  1035.     if (aWindow->displayInstructions & WISH_ATIME_FIELD) {
  1036.     /* 24 chars needed for time, plus 2 spaces == 26 */
  1037.     strcat(fields, "  AccessTime              ");
  1038.     }
  1039.     if (aWindow->displayInstructions & WISH_MTIME_FIELD) {
  1040.     /* 24 chars needed for time, plus 2 spaces == 26 */
  1041.     strcat(fields, "  DataModifyTime          ");
  1042.     }
  1043.     if (aWindow->displayInstructions & WISH_DTIME_FIELD) {
  1044.     /* 24 chars needed for time, plus 2 spaces == 26 */
  1045.     /*
  1046.      * This is one of the few places where Descriptor isn't spelled out -
  1047.      * for space considerations.
  1048.      */
  1049.     strcat(fields, "  DescModifyTime          ");
  1050.     }
  1051.     if (aWindow->displayInstructions & WISH_SIZE_FIELD) {
  1052.     /* 9 chars needed for size, plus 2 spaces == 11 */
  1053.     strcat(fields, "      Bytes");
  1054.     }
  1055.  
  1056.     strcpy(sorting, "Sorted by:  ");
  1057.     if (aWindow->sortingInstructions & WISH_ALPHA_SORT) {
  1058.     if (aWindow->sortingInstructions & WISH_REVERSE_SORT) {
  1059.         strcat(sorting, "Reverse Alphabet");
  1060.     } else {
  1061.         strcat(sorting, "Alphabet"); }
  1062.     }
  1063.     if (aWindow->sortingInstructions & WISH_ATIME_SORT) {
  1064.     if (aWindow->sortingInstructions & WISH_REVERSE_SORT) {
  1065.         strcat(sorting, "Reverse AccessTime");
  1066.     } else {
  1067.         strcat(sorting, "AccessTime");
  1068.     }
  1069.     }
  1070.     if (aWindow->sortingInstructions & WISH_MTIME_SORT) {
  1071.     if (aWindow->sortingInstructions & WISH_REVERSE_SORT) {
  1072.         strcat(sorting, "Reverse DataModifyTime");
  1073.     } else {
  1074.         strcat(sorting, "DataModifyTime");
  1075.     }
  1076.     }
  1077.     if (aWindow->sortingInstructions & WISH_DTIME_SORT) {
  1078.     if (aWindow->sortingInstructions & WISH_REVERSE_SORT) {
  1079.         strcat(sorting, "Reverse DescriptorModifyTime");
  1080.     } else {
  1081.         strcat(sorting, "DescriptorModifyTime");
  1082.     }
  1083.     }
  1084.     if (aWindow->sortingInstructions & WISH_SIZE_SORT) {
  1085.     if (aWindow->sortingInstructions & WISH_REVERSE_SORT) {
  1086.         strcat(sorting, "Reverse Size");
  1087.     } else {
  1088.         strcat(sorting, "Size");
  1089.     }
  1090.     }
  1091.  
  1092. #ifdef SCROLL_DEBUG
  1093.     fprintf(stderr, "Entered WishRedraw:\n");
  1094.     fprintf(stderr, "\tfirstElement is %d\n",
  1095.         aWindow->firstElement);
  1096.     fprintf(stderr, "\tlastElement is %d\n",
  1097.         aWindow->lastElement);
  1098.     fprintf(stderr, "\tnumElements is %d\n",
  1099.         aWindow->numElements);
  1100.     fprintf(stderr, "\tnumGroups is %d\n", aWindow->numGroups);
  1101. #endif SCROLL_DEBUG
  1102.  
  1103.     XClearArea(wishDisplay, aWindow->displayWindow, 0, 0, 0, 0, False);
  1104.  
  1105.     for (tmpGroupPtr = aWindow->groupList;
  1106.         tmpGroupPtr != NULL; tmpGroupPtr = tmpGroupPtr->nextPtr) {
  1107.     for (tmpPtr = tmpGroupPtr->fileList;
  1108.         tmpPtr != NULL; tmpPtr = tmpPtr->nextPtr) {
  1109.         WishRedrawFile(aWindow, tmpPtr);
  1110.         somethingDrawnP = TRUE;
  1111.     }
  1112.     }
  1113.     /*
  1114.      * Now draw the column dividers.
  1115.      */
  1116.     if (somethingDrawnP) {
  1117.     for (i = 1; i <= aWindow->usedCol; i++) {
  1118.         XDrawLine(wishDisplay, aWindow->displayWindow,
  1119.             aWindow->textGc,
  1120.             aWindow->columns[i].x, 0,
  1121.             aWindow->columns[i].x, aWindow->windowHeight);
  1122.     }
  1123.     }
  1124.     /*
  1125.      * Now set the scroll bar to match what will be displayed.
  1126.      */
  1127.     if (aWindow->numRows == 0) {
  1128.     top = 0.0;
  1129.     bottom = 1.0;
  1130.     } else {
  1131.     /*
  1132.      * First (last) entry divided by number of entries required.  (This
  1133.      * includes any displayed spaces and groups.)
  1134.      */
  1135.     top = ((float) aWindow->firstElement - 1) /
  1136.         ((float) aWindow->totalDisplayEntries);
  1137.     bottom = ((float) aWindow->lastElement) /
  1138.         ((float) aWindow->totalDisplayEntries);
  1139.     }
  1140. #ifdef SCROLL_DEBUG
  1141.     fprintf(stderr, "At end of WishRedraw:\n");
  1142.     fprintf(stderr, "\ttop is %f\n", top);
  1143.     fprintf(stderr, "\tbottom is %f\n", bottom);
  1144.     fprintf(stderr, "\tcalling SetRange.\n");
  1145. #endif SCROLL_DEBUG
  1146.  
  1147.     Sx_ScrollbarSetRange(wishDisplay, aWindow->scrollWindow, top, bottom);
  1148.     Sx_TitleMake(wishDisplay, aWindow->sortWindow, aWindow->fontPtr,
  1149.         aWindow->sortForeground, aWindow->sortBackground,
  1150.         aWindow->sortBackground, sorting, NULL, NULL);
  1151.     Sx_TitleMake(wishDisplay, aWindow->fieldsWindow, aWindow->fontPtr,
  1152.         aWindow->fieldsForeground, aWindow->fieldsBackground,
  1153.         aWindow->fieldsBackground, fields, NULL, NULL);
  1154.  
  1155.     return;
  1156. }
  1157.  
  1158.  
  1159.  
  1160. /*
  1161.  *----------------------------------------------------------------------
  1162.  *
  1163.  * WishRedrawFile --
  1164.  *
  1165.  *    Redraw a file entry.  Highlight with a box, or indicate that it is
  1166.  *    selected, if required.
  1167.  *
  1168.  * Results:
  1169.  *    None.
  1170.  *
  1171.  * Side effects:
  1172.  *    The file entry is redisplayed.
  1173.  *
  1174.  *----------------------------------------------------------------------
  1175.  */
  1176. void
  1177. WishRedrawFile(aWindow, filePtr)
  1178.     WishWindow    *aWindow;
  1179.     WishFile        *filePtr;
  1180. {
  1181.     char    *space = NULL;
  1182.     int        secondFieldStart;
  1183.     int        ascent;
  1184.     int        lbearing;    /* to indent string enough that we don't
  1185.                  * draw a box over its first char */
  1186.  
  1187.     if (aWindow->notifierP) {
  1188.     return;
  1189.     }
  1190.     if (filePtr->x == -1) {        /* is it visible? */
  1191.     return;
  1192.     }
  1193.     ascent = aWindow->fontPtr->ascent;
  1194.     lbearing =  -4;
  1195.     if ((aWindow->displayInstructions & ~WISH_NAME_FIELD) != 0) {
  1196.     space = (char *) malloc(aWindow->maxEntryWidth + 1 -
  1197.         aWindow->maxNameLength);
  1198.     space[0] = '\0';
  1199.     WishGetFileFields(aWindow, filePtr, &space);
  1200.     }
  1201.  
  1202.     secondFieldStart =
  1203.         WISH_CHAR_TO_WIDTH(aWindow->maxNameLength + 2, aWindow->fontPtr);
  1204.     if (filePtr->selectedP || filePtr->myGroupPtr->selectedP) {
  1205.     /* should highlight */
  1206.     XDrawImageString(wishDisplay, aWindow->displayWindow,
  1207.         aWindow->reverseGc, filePtr->x + -lbearing + 1,
  1208.         filePtr->y + ascent, filePtr->name, strlen(filePtr->name));
  1209.     if (space != NULL) {
  1210.         if (filePtr->lineP) {
  1211.         XDrawImageString(wishDisplay, aWindow->displayWindow,
  1212.             aWindow->reverseGc,
  1213.             filePtr->x + secondFieldStart,
  1214.             filePtr->y + ascent, space, strlen(space));
  1215.         } else {
  1216.         XDrawImageString(wishDisplay, aWindow->displayWindow,
  1217.             aWindow->textGc,
  1218.             filePtr->x + secondFieldStart,
  1219.             filePtr->y + ascent, space, strlen(space));
  1220.         }
  1221.     }
  1222.     } else {
  1223.     XDrawImageString(wishDisplay, aWindow->displayWindow, aWindow->textGc,
  1224.         filePtr->x + -lbearing + 1, filePtr->y + ascent, filePtr->name,
  1225.         strlen(filePtr->name)); 
  1226.     if (space != NULL) {
  1227.         XDrawImageString(wishDisplay, aWindow->displayWindow,
  1228.             aWindow->textGc, filePtr->x + secondFieldStart,
  1229.             filePtr->y + ascent, space, strlen(space));
  1230.     }
  1231.     }
  1232.  
  1233.     /* draw or undraw a box around it */
  1234.     {
  1235.     XPoint    vlist[5];
  1236.  
  1237.     vlist[0].x = filePtr->x + 1;
  1238.     vlist[0].y = filePtr->y + aWindow->rowHeight;
  1239.  
  1240.     /*
  1241.      * To make up for indentation of string by -lbearing amount.
  1242.      */
  1243.     vlist[1].x = -lbearing +
  1244.         WISH_CHAR_TO_WIDTH(aWindow->maxNameLength, aWindow->fontPtr);
  1245.     vlist[1].y = 0;
  1246.  
  1247.     vlist[2].x = 0;
  1248.     vlist[2].y = -(aWindow->rowHeight);
  1249.  
  1250.     vlist[3].x = -(vlist[1].x);
  1251.     vlist[3].y = 0;
  1252.  
  1253.     /* should be equal to first point - if not, make all rel. to origin. */
  1254.     vlist[4].x = 0;
  1255.     vlist[4].y = -(vlist[2].y);
  1256.  
  1257.     if (filePtr->highlightP) {
  1258.         /* should be selectionGc */
  1259.         XDrawLines(wishDisplay, aWindow->displayWindow,
  1260.             aWindow->textGc, vlist, 5, CoordModePrevious);
  1261.     } else {
  1262.         XDrawLines(wishDisplay, aWindow->displayWindow,
  1263.             aWindow->reverseGc, vlist, 5, CoordModePrevious);
  1264.     }
  1265.     }
  1266.     if (space != NULL) {
  1267.     free(space);
  1268.     }
  1269.  
  1270.     return;
  1271. }
  1272.  
  1273.  
  1274. /*
  1275.  *----------------------------------------------------------------------
  1276.  *
  1277.  * WishGetFileFields --
  1278.  *
  1279.  *    Get the additional fields to display for a file entry beyond the
  1280.  *    file name.
  1281.  *    If *buf is null, then allocate space for the string to return.
  1282.  *    Otherwise copy the return value into *buf.
  1283.  *
  1284.  * Results:
  1285.  *    None.
  1286.  *
  1287.  * Side effects:
  1288.  *    The file entry is redisplayed.
  1289.  *
  1290.  *----------------------------------------------------------------------
  1291.  */
  1292. void
  1293. WishGetFileFields(aWindow, filePtr, buf)
  1294.     WishWindow    *aWindow;
  1295.     WishFile        *filePtr;
  1296.     char        **buf;
  1297. {
  1298.     char    timeBuf[26];    /* 26 chars according to ctime man page */
  1299.     char    intBuf[CVT_INT_BUF_SIZE + 1]; /* const defined in my .h file */
  1300.     int        i;
  1301.     char    *space;
  1302.  
  1303.     /* What to do here? */
  1304.     if (*buf == NULL) {
  1305.     *buf = (char *) malloc(aWindow->maxEntryWidth + 1 -
  1306.         aWindow->maxNameLength);
  1307.     *buf[0] = '\0';
  1308.     }
  1309.     space = *buf;
  1310.     /* Any fields except for name? */
  1311.     if (!(aWindow->displayInstructions & (~WISH_NAME_FIELD))) {
  1312.     /* Could free up space from attrPtr field here, if I want. */
  1313.     return;
  1314.     }
  1315.     if (filePtr->attrPtr == NULL) {
  1316.     filePtr->attrPtr = (struct stat *) malloc(sizeof (struct stat));
  1317.     if (lstat(filePtr->name, filePtr->attrPtr) != 0) {
  1318.         sprintf(wishErrorMsg, "%s %s.  %s.",
  1319.             "Couldn't get attributes for file",
  1320.             filePtr->name,  "The file may no longer exist");
  1321.         aWindow->notifierP = TRUE;
  1322.         Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
  1323.             wishErrorMsg, NULL, TRUE, "Continue",
  1324.             (char *) NULL);
  1325.         aWindow->notifierP = FALSE;
  1326.         bzero(filePtr->attrPtr, sizeof (struct stat)); 
  1327.         strcat(space, "  X  ");
  1328.         return;
  1329.     }
  1330.     }
  1331.     if (aWindow->displayInstructions & WISH_ATIME_FIELD) {
  1332.     /* 24 chars needed for time, plus 2 spaces == 26 */
  1333.     strcpy(timeBuf, ctime(&(filePtr->attrPtr->st_atime)));
  1334.     /* wasteful to do twice, I suppose */
  1335.     if (index(timeBuf, '\n') != NULL) {
  1336.         *(index(timeBuf, '\n')) = '\0';
  1337.     }
  1338.     strcat(space, timeBuf);
  1339.     strcat(space, "  ");
  1340.     }
  1341.     if (aWindow->displayInstructions & WISH_MTIME_FIELD) {
  1342.     /* 24 chars needed for time, plus 2 spaces */
  1343.     strcpy(timeBuf, ctime(&(filePtr->attrPtr->st_mtime)));
  1344.     /* wasteful to do twice, I suppose */
  1345.     if (index(timeBuf, '\n') != NULL) {
  1346.         *(index(timeBuf, '\n')) = '\0';
  1347.     }
  1348.     strcat(space, timeBuf);
  1349.     strcat(space, "  ");
  1350.     }
  1351.     if (aWindow->displayInstructions & WISH_DTIME_FIELD) {
  1352.     /* 24 chars needed for time, plus 2 spaces */
  1353.     strcpy(timeBuf, ctime(&(filePtr->attrPtr->st_ctime)));
  1354.     /* wasteful to do twice, I suppose */
  1355.     if (index(timeBuf, '\n') != NULL) {
  1356.         *(index(timeBuf, '\n')) = '\0';
  1357.     }
  1358.     strcat(space, timeBuf);
  1359.     strcat(space, "  ");
  1360.     }
  1361.     if (aWindow->displayInstructions & WISH_SIZE_FIELD) {
  1362.     /* 9 chars needed for size, plus 2 spaces */
  1363.     sprintf(intBuf, "%d", filePtr->attrPtr->st_size);
  1364.     for (i = 0; i < 9 - strlen(intBuf); i++) {
  1365.         strcat(space, " ");
  1366.     }
  1367.     strcat(space, intBuf);
  1368.     strcat(space, "  ");
  1369.     }
  1370. #ifdef NOTDEF
  1371.     if (aWindow->displayInstructions & WISH_OTHERSTUFF_FIELD) {
  1372.     /* 9 chars needed for size, plus 2 spaces */
  1373.     }
  1374. #endif NOTDEF
  1375.  
  1376.     return;
  1377. }
  1378.  
  1379.  
  1380. /*
  1381.  *----------------------------------------------------------------------
  1382.  *
  1383.  * WishSetWindowAndRowInfo --
  1384.  *
  1385.  *    Given the dimensions of the window, calculate
  1386.  *    the height of rows and the number of usable rows in the window,
  1387.  *    and set these fields (and the window dimension fields) in the
  1388.  *    given WishWindow structure.  The row height calculation is
  1389.  *    made relative to the current font height specified in the fontPtr
  1390.  *    field of the WishWindow structure.
  1391.  *
  1392.  * Results:
  1393.  *    None.
  1394.  *
  1395.  * Side effects:
  1396.  *    The fields in the WishWindow structure that are set are
  1397.  *    the rowHeight, numRows, windowHeight and windowWidth fields.
  1398.  *
  1399.  *----------------------------------------------------------------------
  1400.  */
  1401. void
  1402. WishSetWindowAndRowInfo(aWindow, height, width)
  1403.     WishWindow    *aWindow;
  1404.     int            height, width;
  1405. {
  1406.     aWindow->windowHeight = height;
  1407.     aWindow->windowWidth = width;
  1408.     aWindow->rowHeight = aWindow->fontPtr->max_bounds.ascent +
  1409.         aWindow->fontPtr->max_bounds.descent + WISH_ROW_SPACING;
  1410.     aWindow->numRows = aWindow->windowHeight / aWindow->rowHeight;
  1411.  
  1412.     return;
  1413. }
  1414.  
  1415.  
  1416.  
  1417. /*
  1418.  *----------------------------------------------------------------------
  1419.  *
  1420.  * WishRedrawGroup --
  1421.  *
  1422.  *    Not yet implemented.
  1423.  *
  1424.  * Results:
  1425.  *    None.
  1426.  *
  1427.  * Side effects:
  1428.  *    None.
  1429.  *
  1430.  *----------------------------------------------------------------------
  1431.  */
  1432. /*ARGSUSED*/
  1433. void
  1434. WishRedrawGroup(aWindow, groupPtr)
  1435.     WishWindow    *aWindow;
  1436.     WishGroup        *groupPtr;
  1437. {
  1438.     return;
  1439. }
  1440.  
  1441.  
  1442. /*
  1443.  *----------------------------------------------------------------------
  1444.  *
  1445.  * WishMapCoordsToFile --
  1446.  *
  1447.  *    Map the given coordinates to a file entry.  The mapping will work
  1448.  *    if the cursor is on the same line as a file entry.  It does not
  1449.  *    have to be directory over the file entry.
  1450.  *
  1451.  * Results:
  1452.  *    A pointer to the appropriate file entry, or NULL if there isn't
  1453.  *    one under the given coordinates.    
  1454.  *
  1455.  * Side effects:
  1456.  *    None.
  1457.  *
  1458.  *----------------------------------------------------------------------
  1459.  */
  1460. WishFile *
  1461. WishMapCoordsToFile(aWindow, x, y)
  1462.     WishWindow    *aWindow;
  1463.     int            x, y;
  1464. {
  1465.     WishGroup    *groupPtr;
  1466.     WishFile    *filePtr;
  1467.     int        ascent;
  1468.  
  1469.     ascent = aWindow->fontPtr->ascent;
  1470.     for (groupPtr = aWindow->groupList; groupPtr != NULL;
  1471.         groupPtr = groupPtr->nextPtr) {
  1472.     for (filePtr = groupPtr->fileList; filePtr != NULL;
  1473.         filePtr = filePtr->nextPtr) {
  1474.         if (filePtr->x == -1) {    /* off the display */
  1475.         continue;
  1476.         }
  1477.         if ((filePtr->x <= x && x <= filePtr->x + aWindow->columnWidth) &&
  1478.             (filePtr->y + ascent <= y && y <= filePtr->y + ascent +
  1479.             aWindow->rowHeight)) {
  1480.         return filePtr;
  1481.         }
  1482.     }
  1483.     }
  1484.  
  1485.     return NULL;
  1486. }
  1487.  
  1488.  
  1489. /*
  1490.  *----------------------------------------------------------------------
  1491.  *
  1492.  * WishMapCoordsToGroup --
  1493.  *
  1494.  *    Map the given coordinates to a group header.
  1495.  *
  1496.  * Results:
  1497.  *    A pointer to the appropriate group header, or NULL if there isn't
  1498.  *    one under the given coordinates.
  1499.  *
  1500.  * Side effects:
  1501.  *    None.
  1502.  *
  1503.  *----------------------------------------------------------------------
  1504.  */
  1505. WishGroup *
  1506. WishMapCoordsToGroup(aWindow, x, y)
  1507.     WishWindow    *aWindow;
  1508.     int            x, y;
  1509. {
  1510.     WishGroup    *groupPtr;
  1511.  
  1512.     for (groupPtr = aWindow->groupList; groupPtr != NULL;
  1513.         groupPtr = groupPtr->nextPtr) {
  1514.     if ((groupPtr->x <= x && x <= groupPtr->x + groupPtr->length) &&
  1515.         (groupPtr->y <= y && y <= groupPtr->y + aWindow->rowHeight)) {
  1516.         return groupPtr;
  1517.     }
  1518.     }
  1519.  
  1520.     return NULL;
  1521. }
  1522.  
  1523.  
  1524.  
  1525. /*
  1526.  *----------------------------------------------------------------------
  1527.  *
  1528.  * GetMaxEntryWidth --
  1529.  *
  1530.  *    Find the length of the longest column entry.
  1531.  *
  1532.  * Results:
  1533.  *    The length.
  1534.  *
  1535.  * Side effects:
  1536.  *    None.
  1537.  *
  1538.  *----------------------------------------------------------------------
  1539.  */
  1540. static int
  1541. GetMaxEntryWidth(aWindow)
  1542.     WishWindow    *aWindow;
  1543. {
  1544.     WishGroup    *tmpGroupPtr;
  1545.     WishFile    *tmpFilePtr;
  1546.     Boolean    getAttrsP = FALSE;
  1547.     int        charLength = 0;
  1548.     int        lbearing;
  1549.  
  1550.     if ((aWindow->displayInstructions & ~WISH_NAME_FIELD) != 0) {
  1551.     getAttrsP = TRUE;
  1552.     }
  1553.     lbearing = -4;
  1554.     aWindow->maxNameLength = 0;
  1555.     /*
  1556.      * Warning: this allocates space width for the rules, even if they
  1557.      * are hidden groups.
  1558.      */
  1559.     for (tmpGroupPtr = aWindow->groupList; tmpGroupPtr != NULL;
  1560.         tmpGroupPtr = tmpGroupPtr->nextPtr) {
  1561.     charLength = strlen(tmpGroupPtr->rule);
  1562.     tmpGroupPtr->length =
  1563.         XTextWidth(aWindow->fontPtr, tmpGroupPtr->rule, charLength);
  1564.     if (charLength > aWindow->maxNameLength) {
  1565.         aWindow->maxNameLength = charLength;
  1566.     }
  1567.     for (tmpFilePtr = tmpGroupPtr->fileList; tmpFilePtr != NULL;
  1568.         tmpFilePtr = tmpFilePtr->nextPtr) {
  1569.         charLength = strlen(tmpFilePtr->name);
  1570.         tmpFilePtr->length =
  1571.             XTextWidth(aWindow->fontPtr, tmpFilePtr->name, charLength);
  1572.         if (charLength > aWindow->maxNameLength) {
  1573.         aWindow->maxNameLength = charLength;
  1574.         }
  1575.     }
  1576.     }
  1577.     aWindow->maxEntryWidth = aWindow->maxNameLength;
  1578.     /* 2 chars for spaces */
  1579.     aWindow->maxEntryWidth += 2;
  1580.     if (getAttrsP) {
  1581.     if (aWindow->displayInstructions & WISH_ATIME_FIELD) {
  1582.         /* 24 chars needed for time, plus 2 spaces */
  1583.         aWindow->maxEntryWidth += 26;
  1584.     }
  1585.     if (aWindow->displayInstructions & WISH_MTIME_FIELD) {
  1586.         /* 24 chars needed for time, plus two spaces */
  1587.         aWindow->maxEntryWidth += 26;
  1588.     }
  1589.     if (aWindow->displayInstructions & WISH_DTIME_FIELD) {
  1590.         /* 24 chars needed for time, plus two spaces */
  1591.         aWindow->maxEntryWidth += 26;
  1592.     }
  1593.     if (aWindow->displayInstructions & WISH_SIZE_FIELD) {
  1594.         /* 9 chars needed for time, plus two spaces */
  1595.         aWindow->maxEntryWidth += 11;
  1596.     }
  1597. #ifdef NOTDEF
  1598.     /* what to do about owner sizes, groups, plus perm. display? */
  1599.     if (aWindow->displayInstructions & WISH_PERM_FIELD) {
  1600.         /* 1 chars needed for time, plus two spaces */
  1601.         aWindow->maxEntryWidth += 12;
  1602.     }
  1603.     if (aWindow->displayInstructions & WISH_TYPE_FIELD) {
  1604.         /* 1 chars needed for time, plus two spaces */
  1605.         aWindow->maxEntryWidth += 12;
  1606.     }
  1607. #endif NOTDEF
  1608.     }
  1609.  
  1610.     /*
  1611.      * Plus lbearing cause we want to indent string enough that we don't draw
  1612.      * a box around it.
  1613.      */
  1614.     return (-lbearing +
  1615.         WISH_CHAR_TO_WIDTH(aWindow->maxEntryWidth, aWindow->fontPtr));
  1616. }
  1617.  
  1618.  
  1619. /*
  1620.  *----------------------------------------------------------------------
  1621.  *
  1622.  * GetColumnWidthInfo --
  1623.  *
  1624.  *    Figure out the best fixed column width.  This includes the
  1625.  *    extra WISH_COLUMN_SPACING added.  Also figure out the number
  1626.  *    of columns.
  1627.  *
  1628.  * Results:
  1629.  *    None.
  1630.  *
  1631.  * Side effects:
  1632.  *    The columnWidth and numCols parameters are filled in with their values.
  1633.  *
  1634.  *----------------------------------------------------------------------
  1635.  */
  1636. static void
  1637. GetColumnWidthInfo(aWindow, maxEntryWidth, columnWidth, numCols)
  1638.     WishWindow    *aWindow;
  1639.     int            maxEntryWidth;
  1640.     int            *columnWidth;
  1641.     int            *numCols;
  1642. {
  1643.     int        numToDisplay;
  1644.     int        numColsRequired;
  1645.     int        widthLeft;
  1646.  
  1647.     if (maxEntryWidth < 0) {
  1648.     *columnWidth = GetMaxEntryWidth(aWindow) + WISH_COLUMN_SPACING;
  1649.     } else {
  1650.     *columnWidth = maxEntryWidth + WISH_COLUMN_SPACING;
  1651.     }
  1652.     *numCols = aWindow->windowWidth / *columnWidth;
  1653.     if (*numCols == 0) {
  1654.     return ;
  1655.     }
  1656.     numToDisplay = aWindow->totalDisplayEntries - aWindow->firstElement + 1;
  1657.     if (aWindow->numRows == 0) {
  1658.     return ;
  1659.     }
  1660.     numColsRequired = numToDisplay / aWindow->numRows;
  1661.     if (numToDisplay % aWindow->numRows != 0) {
  1662.     numColsRequired++;
  1663.     }
  1664.     if (numColsRequired < *numCols) {
  1665.     *numCols = numColsRequired;
  1666.     }
  1667.     if (*numCols == 0) {
  1668.     return ;
  1669.     }
  1670.     widthLeft = aWindow->windowWidth - (*numCols * *columnWidth);
  1671.     /*
  1672.      * This could allow the last column to fail to meet the scroll bar by
  1673.      * a number of pixels one less than the number of columns.
  1674.      * We also add 1 pixel here so that entry window borders will overlap
  1675.      * with the window borders if we start them at 1 pixel before the
  1676.      * actual text area.
  1677.      */
  1678.     *columnWidth += (widthLeft / *numCols) + 1;
  1679.  
  1680.     return ;
  1681. }
  1682.  
  1683.  
  1684. /*
  1685.  *----------------------------------------------------------------------
  1686.  *
  1687.  * FigureWindowSize --
  1688.  *
  1689.  *    Figure out a good window size.
  1690.  *
  1691.  * Results:
  1692.  *    None.
  1693.  *
  1694.  * Side effects:
  1695.  *    The window size changes and stuff is laid out again to match.
  1696.  *
  1697.  *----------------------------------------------------------------------
  1698.  */
  1699. static void
  1700. FigureWindowSize(aWindow, maxEntryWidth)
  1701.     WishWindow    *aWindow;
  1702.     int            maxEntryWidth;
  1703. {
  1704.     int        maxWidth;
  1705.     int        width, height;
  1706.     int        columnWidth, numCols, numRows, numColsRequired;
  1707.  
  1708.     columnWidth = maxEntryWidth;
  1709.  
  1710.     maxWidth = wishRootWidth / 8 * 7;
  1711.     width = maxWidth;
  1712.  
  1713.     numCols = width / (columnWidth + WISH_COLUMN_SPACING);
  1714.     if (numCols == 0) {
  1715.     /* just set to max size */
  1716.     }
  1717.  
  1718.     for ( ; ; ){
  1719.     /* Make it line up on column boundary -- no wasted space */
  1720.     width = numCols  * (columnWidth + WISH_COLUMN_SPACING); 
  1721.     height = width / 3 * 2;        /* 2/3's of width */
  1722.     numRows = height / aWindow->rowHeight;
  1723.     if (aWindow->numRows == 0) {
  1724.         /* just set to max size */
  1725.     }
  1726.     numColsRequired = aWindow->totalDisplayEntries / numRows;
  1727.  
  1728.     if (aWindow->totalDisplayEntries % numRows != 0) {
  1729.         numColsRequired++;
  1730.     }
  1731.     if (numColsRequired < numCols) {
  1732.         numCols--;
  1733.     } else {
  1734.         break;
  1735.     }
  1736.     }
  1737.     /* last shrinking may have gone too far since we shrank rows as well */
  1738.     if (numColsRequired > numCols && (width + columnWidth +
  1739.         WISH_COLUMN_SPACING <= maxWidth)) {
  1740.     numCols++;
  1741.     width += columnWidth + WISH_COLUMN_SPACING;
  1742.     /* leave height and numRows alone */
  1743.     }
  1744.     /*
  1745.      * This next will generate an ExposureMask event, but I'm not sure
  1746.      * yet just where it fit in, so I may change some things.
  1747.      * It is an incredible pain that I can't change the displayWindow
  1748.      * to the correct dimensions and have the packer know to resize the
  1749.      * parent window.  Right now I have to resize to parent (surrounding)
  1750.      * window to what I guess should be its size.  I can't
  1751.      * XGetGeometry() them here since they all seem to have a size of 1
  1752.      * in Sx until some event occurs...  IS THIS STILL TRUE IN X11???
  1753.      */
  1754.     /*
  1755.      * 2 titles, 2 regular bars, and 8 regular font heights for tx window.
  1756.      */
  1757.     if (aWindow->windowWidth != width || aWindow->windowHeight != height) {
  1758.     XWindowChanges    changes;
  1759.  
  1760.     changes.width = width + 1 + Sx_ScrollbarWidth();
  1761.     changes.height = height + 
  1762.         (2 * Sx_DefaultHeight(wishDisplay, aWindow->titleFontPtr))
  1763.         + 3 + (10 * Sx_DefaultHeight(wishDisplay, aWindow->fontPtr));
  1764.     XConfigureWindow(wishDisplay, aWindow->surroundingWindow,
  1765.         CWWidth | CWHeight, &changes);
  1766.     }
  1767.  
  1768.     return;
  1769. }
  1770.  
  1771.  
  1772. /*
  1773.  *----------------------------------------------------------------------
  1774.  *
  1775.  * WishDumpState --
  1776.  *
  1777.  *    Dump the contents of the group and file lists to stderr for debugging.
  1778.  *
  1779.  * Results:
  1780.  *    None.
  1781.  *
  1782.  * Side effects:
  1783.  *    Stuff gets printed to stderr.
  1784.  *
  1785.  *----------------------------------------------------------------------
  1786.  */
  1787. void
  1788. WishDumpState(aWindow)
  1789.     WishWindow    *aWindow;
  1790. {
  1791.     WishGroup    *tmpGroupPtr;
  1792.     WishFile    *tmpFilePtr;
  1793.  
  1794.     for (tmpGroupPtr = aWindow->groupList; tmpGroupPtr != NULL;
  1795.         tmpGroupPtr = tmpGroupPtr->nextPtr) {
  1796.     fprintf(stderr, "GROUP:\t%s\n", tmpGroupPtr->editHeader);
  1797.     for (tmpFilePtr = tmpGroupPtr->fileList; tmpFilePtr != NULL;
  1798.         tmpFilePtr = tmpFilePtr->nextPtr) {
  1799.         fprintf(stderr, "\t\t%s %s\n", tmpFilePtr->name,
  1800.             tmpFilePtr->attrPtr == NULL ? "NULL" : "attrs");
  1801.     }
  1802.     }
  1803.     return;
  1804. }
  1805.